var canvas;
var gl;
var program;
var isMove = false;
var mX;
var mY;
var rotateQ = rotateq(0, vec3());
var rotateQLocation;
var scaleTimes = 0;
var scaleMatrix = mat4();
var scaleMatrixLocation;
var pointsToDraw = [];
var colorOfPoints = [];
var indicesToDraw = [];

window.onload = init;

function init() {
    canvas = $("#gl-canvas")[0];
    // 初始化WebGL上下文
    gl = WebGLUtils.setupWebGL(canvas);
  
    // 确认WebGL支持性
    if (!gl) {
      alert("无法初始化WebGL，你的浏览器、操作系统或硬件等可能不支持WebGL。");
      return;
    }
  
    // 设置视口
    gl.viewport(0, 0, canvas.width, canvas.height);
    // 填充背景色
    gl.clearColor(0.53, 0.81, 0.92, 1.0);
    
    gl.enable(gl.DEPTH_TEST);

    // 加载着色器
    program = initShaders(gl, "vertex-shader", "fragment-shader");
    gl.useProgram(program);

    
    // 绘制元素
    drawScene();

    // 发送至GPU
    bufferPoints();

    // 渲染
    render();

    // 绑定事件
    document.onmousemove = mouseMoveListener;
    canvas.onmousedown = mouseDownListener;
    document.onmouseup = mouseUpListener;
    canvas.onmousewheel = mouseWheelListener;
}

function render() {
    // 清除缓冲区
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    // 设置uniform变量
    gl.uniform4fv(rotateQLocation, flatten(rotateQ));
    gl.uniformMatrix4fv(scaleMatrixLocation, false, flatten(scaleMatrix));
    // 绘制
    gl.drawElements(gl.TRIANGLES, indicesToDraw.length, gl.UNSIGNED_SHORT, 0);
}

function bufferPoints() {
    // 写入顶点
    // 绑定缓冲区
    var vbuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vbuffer);
    // 写入数据
    gl.bufferData(gl.ARRAY_BUFFER, flatten(pointsToDraw), gl.STATIC_DRAW);
    // 设置着色器变量
    var vLocation = gl.getAttribLocation(program, "vPosition");
    gl.vertexAttribPointer(vLocation, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(vLocation);




    // 写入颜色
    // 绑定缓冲区
    var cbuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, cbuffer);
    // 写入数据
    gl.bufferData(gl.ARRAY_BUFFER, flatten(colorOfPoints), gl.STATIC_DRAW);
    // 设置着色器变量
    var cLocation = gl.getAttribLocation(program, "vColor");
    gl.vertexAttribPointer(cLocation, 4, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(cLocation);




    // 写入索引
    // 绑定缓冲区
    var ibuffer = gl.createBuffer();
    gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibuffer);
    // 写入数据
    gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indicesToDraw), gl.STATIC_DRAW);

    // 设置uniform变量位置
    rotateQLocation = gl.getUniformLocation(program, "r");
    scaleMatrixLocation = gl.getUniformLocation(program, "scaleM");
}

function drawScene() {
    // define basic operation
    var r = rotate(-90, vec3(1,0,0));

    // draw the big ball
    var ball1 = sphere(vec3(0,0,-0.4), 0.4, vec4(1,1,1,1), vec4(0.7,1,1,1));
    
    //draw the small ball
    var ball2 = sphere(vec3(0,0,0.15), 0.25, vec4(1,1,1,1), vec4(0.7,1,1,1));

    // draw her hat
    var hat = cone(vec3(0,0,0.35), 0.15, 0.3, vec4(1,0.2,0.2,1), vec4(1,0.75,0.8,1));

    // draw the small ball on her hat
    var ball3 = sphere(vec3(0,0,0.65), 0.04, vec4(1,1,1,1), vec4(1,1,1,1));

    // draw her nose
    var nose = cone(vec3(0,-0.15,0.2), 0.04, 0.25, vec4(1,0.55,0,1), vec4(1,0.2,0.2,1));
    mult4object(r, nose);

    // draw her eyes
    var l_eye = sphere(vec3(-0.1,0.22,0.21), 0.02, vec4(0,0,0,1), vec4(0,0,0,1));
    var r_eye = sphere(vec3(0.1,0.22,0.21), 0.02, vec4(0,0,0,1), vec4(0,0,0,1));

    // draw her hands
    var l_hand = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1));
    var r_l = rotate(45, vec3(0,1,0));
    mult4object(r_l, l_hand);
    var t_l = translate(0.3,0,-0.1);
    mult4object(t_l, l_hand);

    var r_hand = cuboid(0.015, 0.015, 0.6, vec4(0.54,0.27,0.07,1));
    var r_r = rotate(-45, vec3(0,1,0));
    mult4object(r_r, r_hand);
    var t_r = translate(-0.3,0,-0.1);
    mult4object(t_r, r_hand);

    // final rotate
    mult4object(r, pointsToDraw);
}

function mouseDownListener(event) {
    isMove = event.button==0?true:false;
    if (event.button==1) { // 中键复原
        rotateQ = vec4(1,0,0,0);
        scaleTimes = 0;
        scaleMatrix = mat4();
        render();
    }
    mX = event.clientX;
    mY = event.clientY;
}

function mouseUpListener(event) {
    isMove = false;
}

function mouseMoveListener(event) {
    if (!isMove) {
        return;
    }
    dx = event.clientX - mX;
    dy = event.clientY - mY;

    v = vec3(dy, dx, 0);
    angle = Math.sqrt(dx*dx + dy*dy)/100;
    dQ = rotateq(angle, v);
    rotateQ = multq(dQ, rotateQ);
    mX = event.clientX;
    mY = event.clientY;
    render();
}

function mouseWheelListener(event) {
    scaleTimes += event.wheelDelta>0?-1:+1;
    var s = Math.pow(0.9, scaleTimes);
    scaleMatrix = scalem(s,s,s);
    render();
}

function sphere(center, radius, north_color, south_color) { // 绘制球面
    var precision = 100;
    var numOfPoints = pointsToDraw.length;

    // 生成球面点
    for (var i = 0; i < precision; i++) {
        for (var j = 0; j < precision+1; j++) {
            var theta = 2*Math.PI*i/precision;
            var phi = Math.PI*j/precision;
            pointsToDraw.push(vec3(
                center[0] + radius * Math.sin(phi) * Math.cos(theta),
                center[1] + radius * Math.sin(phi) * Math.sin(theta), 
                center[2] + radius * Math.cos(phi)
            ));
            colorOfPoints.push(vec4(
                north_color[0] * (precision - j)/precision + south_color[0] * j/precision,
                north_color[1] * (precision - j)/precision + south_color[1] * j/precision,
                north_color[2] * (precision - j)/precision + south_color[2] * j/precision,
                north_color[3] * (precision - j)/precision + south_color[3] * j/precision
            ));
        }
    }


    // 生成绘图索引
    for (var i=0; i<precision; i++) {
        offsetNextLine = (i==precision-1)?0:(i+1);

        indicesToDraw.push(numOfPoints + i*(precision+1) + 0);
        indicesToDraw.push(numOfPoints + i*(precision+1) + 1);
        indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + 1);

        indicesToDraw.push(numOfPoints + i*(precision+1) + precision);
        indicesToDraw.push(numOfPoints + i*(precision+1) + precision-1);
        indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + precision-1);       

        for (var j=1; j<precision-1; j++) {
            indicesToDraw.push(numOfPoints + i*(precision+1) + j);
            indicesToDraw.push(numOfPoints + i*(precision+1) + j+1);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j);

            indicesToDraw.push(numOfPoints + i*(precision+1) + j+1);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j);
            indicesToDraw.push(numOfPoints + offsetNextLine*(precision+1) + j+1);
        }
    }

    // 将坐标返回
    return pointsToDraw.slice(numOfPoints);
}

function cone(center, radius, height, top_color, bottom_color) { // 绘制圆锥
    var precision = 100;
    var numOfPoints = pointsToDraw.length;

    // 生成锥面点
    // 顶点
    pointsToDraw.push(vec3(
        center[0],
        center[1],
        center[2]+height
    ));
    colorOfPoints.push(top_color);
    // 底面中心
    pointsToDraw.push(center);
    colorOfPoints.push(bottom_color);
    // 底面
    for (var i = 0; i < precision; i++) {
        var theta = 2*Math.PI*i/precision;
        pointsToDraw.push(vec3(
            center[0] + radius * Math.cos(theta),
            center[1] + radius * Math.sin(theta),
            center[2]
        ));
        colorOfPoints.push(bottom_color);
    }

    // 生成绘图索引
    for (var i=2; i<precision+2; i++) {
        offsetNext = (i==precision+1)?2:(i+1);

        indicesToDraw.push(numOfPoints + 0);
        indicesToDraw.push(numOfPoints + i);
        indicesToDraw.push(numOfPoints + offsetNext);

        indicesToDraw.push(numOfPoints + 1);
        indicesToDraw.push(numOfPoints + i);
        indicesToDraw.push(numOfPoints + offsetNext);
    }


    // 将坐标返回
    return pointsToDraw.slice(numOfPoints);
}

function cuboid(x,y,z,color) { // 绘制长方体
    var numOfPoints = pointsToDraw.length;
    
    // 生成长方体顶点
    pointsToDraw = pointsToDraw.concat([
        vec3(-x/2, -y/2, -z/2),
        vec3(+x/2, -y/2, -z/2),
        vec3(+x/2, +y/2, -z/2),
        vec3(-x/2, +y/2, -z/2),
        vec3(-x/2, -y/2, +z/2),
        vec3(+x/2, -y/2, +z/2),
        vec3(+x/2, +y/2, +z/2),
        vec3(-x/2, +y/2, +z/2)
    ]);

    for (var i=0; i<8; i++) {
        colorOfPoints.push(color);
    }

    // 生成绘图索引
    indicesToDraw = indicesToDraw.concat([
        numOfPoints+0, numOfPoints+1, numOfPoints+2,
        numOfPoints+0, numOfPoints+2, numOfPoints+3,
        numOfPoints+1, numOfPoints+2, numOfPoints+5,
        numOfPoints+2, numOfPoints+5, numOfPoints+6,
        numOfPoints+2, numOfPoints+3, numOfPoints+6,
        numOfPoints+3, numOfPoints+6, numOfPoints+7,
        numOfPoints+3, numOfPoints+0, numOfPoints+7,
        numOfPoints+0, numOfPoints+7, numOfPoints+4,
        numOfPoints+0, numOfPoints+1, numOfPoints+4,
        numOfPoints+1, numOfPoints+4, numOfPoints+5,
        numOfPoints+4, numOfPoints+5, numOfPoints+7,
        numOfPoints+5, numOfPoints+7, numOfPoints+6,
    ]);

    // 返回坐标
    return pointsToDraw.slice(numOfPoints);
}

function column(radius, height, color) {
    var numOfPoints = pointsToDraw.length;
    var precision = 100;

    // 生成圆柱面顶点
    for (var i=0; i<2; i++) {
        pointsToDraw.push(vec3(0,0,(i?+1:-1)*height/2));
        colorOfPoints.push(color);
        for (var j=0; j<precision; j++) {
            var theta = 2*Math.PI*j/precision;
            pointsToDraw.push(vec3(
                radius * Math.cos(theta),
                radius * Math.sin(theta),
                (i?+1:-1)*height/2
            ));
            colorOfPoints.push(color);
        }
    }

    // 生成绘制索引
    // 底面
    for (var i=0; i<2; i++) {
        for (var j=1; j<precision+1; j++) {
            indicesToDraw = indicesToDraw.concat([
                i*(precision+1)+0, i*(precision+1)+j, i*(precision+1)+(j==precision?1:j+1)
            ]);
        }
    }

    // 柱面
    for (var i=1; i<precision+1; i++) {
        nextPoint = i==precision?1:i+1;
        indicesToDraw = indicesToDraw.concat([
            i, nextPoint, i+precision+1,
            nextPoint, i+precision+1, nextPoint+precision+1
        ]);
    }

    // 返回坐标
    return pointsToDraw.slice(numOfPoints);
}

function triangularPyramid(p1, p2, p3, p4, c1, c2, c3, c4) {
    var numOfPoints = pointsToDraw.length;
    c1 = vec4(c1);
    c2 = vec4(c2);
    c3 = vec4(c3);
    c4 = vec4(c4);

    // 生成四面体顶点
    pointsToDraw = pointsToDraw.concat([
        p1, p2, p3, p4
    ]);
    colorOfPoints = colorOfPoints.concat([
        c1, c2, c3, c4
    ]);

    // 生成绘图索引
    indicesToDraw = indicesToDraw.concat([
        numOfPoints+0, numOfPoints+1, numOfPoints+2,
        numOfPoints+0, numOfPoints+1, numOfPoints+3,
        numOfPoints+0, numOfPoints+2, numOfPoints+3,
        numOfPoints+1, numOfPoints+2, numOfPoints+3,
    ]);

    // 返回坐标
    return pointsToDraw.slice(numOfPoints);
}

function mult4object(R, P) { // P = RP
    for (var n=0; n<P.length; n++) {
        var p = vec4(P[n]);
        for (var i=0; i<3; i++) {
            P[n][i] = 0;
            for (var j=0; j<4; j++) {
                P[n][i] += R[i][j]*p[j];
            }
        }
    }
}

function multq(a, b)
{
   // vec4(a.x*b.x - dot(a.yzw, b.yzw), a.x*b.yzw+b.x*a.yzw+cross(b.yzw, a.yzw))

   var s = vec3(a[1], a[2], a[3]);
   var t = vec3(b[1], b[2], b[3]);
   return vec4(a[0]*b[0] - dot(s,t), add(cross(t, s), add(scale(a[0],t), scale(b[0],s))));
}

function rotateq(angle, axis) {
    var theta = angle/180*Math.PI;
    return vec4(Math.cos(theta/2), scale(Math.sin(theta/2), axis));
}